﻿using LightCAD.Core.Element3d;
using LightCAD.Core.Elements;
using LightCAD.MathLib;
using System.Buffers;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace LightCAD.Core
{
    public partial class LcDocument
    {

        #region static

        /// <summary>
        /// 创建 LcDocument 对象
        /// </summary>
        /// <returns></returns>
        public static LcDocument Create()
        {
            var doc = new LcDocument();
            doc.Initialize();
            return doc;
        }

        //public static void SetCurrent(LcDocument document)
        //{
        //    LcDocument.Current = document;
        //}

        /// <summary>
        /// 将文件以 json 格式 保持起来
        /// </summary>
        /// <param name="doc"></param>
        /// <param name="filePath"></param>
        /// <returns></returns>
        public static bool Save(LcDocument doc, string filePath)
        {
            Debug.Assert(File.Exists(filePath));

            using var stream = new MemoryStream();
            ToJsonStream(doc, stream, true);
            var json = Encoding.UTF8.GetString(stream.ToArray());
            File.WriteAllText(filePath, json);
            return true;
        }

        /// <summary>
        /// 将文件以 json 格式 序列化
        /// </summary>
        /// <param name="doc"></param>
        /// <param name="indented">输出格式是否需要空格</param>
        /// <returns></returns>
        public static void ToJsonStream(LcDocument doc, Stream stream, bool indented = false)
        {
            var options = new JsonWriterOptions
            {
                Indented = indented
            };
            var soptions = new JsonSerializerOptions
            {
                DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault,
                WriteIndented = indented,
                IncludeFields = true,
            };

            using var writer = new Utf8JsonWriter(stream, options);
            writer.WriteStartObject();
            writer.WriteStringProperty(nameof(Uuid), doc.Uuid);
            writer.WriteStringProperty(nameof(FilePath), doc.FilePath);
            writer.WriteObjectProperty(nameof(Info), doc.Info, soptions);
            writer.WriteObjectProperty(nameof(ProjectInfo), doc.ProjectInfo, soptions);
            writer.WriteVector2dProperty(nameof(InsertPoint), doc.InsertPoint??new Vector2());

            doc.RefreshUsedTypes();
            writer.WriteCollectionProperty(nameof(UsedTypes), doc.UsedTypes, soptions);
            //ObjectPropertiesMap
            //PluginMap
            writer.WriteCollectionProperty(nameof(Components), doc.Components, null, (w, cmt) => w.WriteComponentObject(cmt, soptions));
            writer.WriteCollectionProperty(nameof(UCSs), doc.UCSs, soptions);

            //TextStyle
            writer.WriteCollectionProperty(nameof(Blocks), doc.Blocks, null, (w, blk) => w.WriteBlockObject(blk, soptions));
            //Xrefs

            writer.WriteCollectionProperty(nameof(Layers), doc.Layers, soptions);
            //Views->View3Ds
            //Layouts取消
            writer.WriteCollectionProperty(nameof(Buildings), doc.Buildings, null, (w, bd) => w.WriteBuildingObject(bd, soptions));
            writer.WriteCollectionProperty(nameof(DrawingFrames), doc.DrawingFrames, null, (w, df) => w.WriteDrawingFrameObject(df, soptions));
            //writer.WriteCollectionProperty(nameof(Profiles), doc.Profiles, soptions);

            writer.WriteElementSetProperty(nameof(ModelSpace), doc.ModelSpace, soptions);

            writer.WriteEndObject();

            writer.Flush();

        }



        internal static LcDocument FromJsonStream(Stream stream)
        {
            JsonDocument jsonDoc = JsonDocument.Parse(stream);

            var doc = new LcDocument();
            doc.Initialize();

            var docEle = jsonDoc.RootElement;
            doc.Uuid = docEle.ReadStringProperty(nameof(doc.Uuid));
            doc.FilePath = docEle.ReadStringProperty(nameof(FilePath));
            doc.Info = docEle.ReadObjectProperty<LcDocumentInfo>(nameof(Info));
            doc.ProjectInfo = docEle.ReadObjectProperty<LcProjectInfo>(nameof(ProjectInfo));
            doc.InsertPoint = docEle.ReadVector2dProperty(nameof(InsertPoint));

            var usedTypes = docEle.ReadCollectionProperty<ElementTypeCollection, ElementType>(nameof(UsedTypes),null);
            if (usedTypes != null) { doc.UsedTypes = usedTypes; }

            var components = docEle.ReadCollectionProperty<ComponentCollection, LcComponentDefinition>(nameof(Components), doc);
            if (components != null) { doc.Components = components; }

            var ucss = docEle.ReadCollectionProperty<UCSCollection, LcUCS>(nameof(UCSs), doc);
            if (ucss != null) { doc.UCSs = ucss; }
            //TextStyle

            var blocks = docEle.ReadCollectionProperty<LcBlockCollection, LcBlock>(nameof(Blocks), doc);
            if (blocks != null) { doc.Blocks = blocks; }
            //Xrefs

            var layers = docEle.ReadCollectionProperty<LayerCollection, LcLayer>(nameof(Layers), doc);
            if (layers != null) { doc.Layers = layers; }
            //Views->View3Ds
            //Layouts取消
            var buildings = docEle.ReadCollectionProperty<BuildingCollection, LcBuilding>(nameof(Buildings), doc);
            if (buildings != null) { doc.Buildings = buildings; }

            var drawingFrames = docEle.ReadCollectionProperty<LcDrawingFrameCollection, LcDrawingFrame>(nameof(DrawingFrames), doc);
            if(drawingFrames != null) { doc.DrawingFrames = drawingFrames; }
            //writer.WriteCollectionProperty(nameof(Profiles), doc.Profiles, soptions);

            doc.ModelSpace = doc.CreateObject<LcModelSpace>();
            doc.ModelSpace.Elements = docEle.ReadElementSetProperty(nameof(ModelSpace), doc);
            LcDocument.ResetObjectRelation(doc.ModelSpace.Elements.ToList());
            foreach (var building in doc.Buildings)
            {
                building.ReadObjectRefs(doc.ModelSpace.Elements);
            }
            return doc;
        }

        public static void ToComponentJsonStream(LcDocument doc, Stream stream, bool indented = false)
        {
            var options = new JsonWriterOptions
            {
                Indented = indented
            };
            var soptions = new JsonSerializerOptions
            {
                DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault,
                WriteIndented = indented,
                IncludeFields = true,
            };

            using var writer = new Utf8JsonWriter(stream, options);
            writer.WriteStartObject();

            var cptDefs = ComponentManager.GetExternalCptDefs();
            writer.WriteCollectionProperty("Components", cptDefs, null, (w, cmt) =>
            {
                writer.WriteStartObject();
                cmt.WriteProperties(writer, soptions);
                writer.WriteEndObject();
            });

            var cptIns = doc.ModelSpace.Elements.Where(e => e is IComponentInstance).ToList();
            writer.WriteCollectionProperty("ComponentInstances", cptIns, null, (w, cmt) =>
            {
                writer.WriteElementObject(cmt, soptions);
            });

            writer.WriteEndObject();

            writer.Flush();
        }

        public static void FromComponentJsonStream(LcDocument doc, Stream stream)
        {
            JsonDocument jsonDoc = JsonDocument.Parse(stream);

            var docEle = jsonDoc.RootElement;
            if (docEle.TryGetProperty("Components", out var prop))
            {
                var arr = prop.EnumerateArray().ToArray();
                for (int i = 0; i < arr.Length; i++)
                {
                    var item = arr[i];
                    LcComponentDefinition cptDef = null;
                    string type = item.ReadStringProperty("TypeName");
                    if (type == "LcLoftDef")
                    {
                        cptDef = new LcLoftDef(null, null, null);
                    }
                    else if (type == "LcExtrudeDef")
                    {
                        cptDef = new LcExtrudeDef(null, null, null);
                    }
                    cptDef.ReadProperties(ref item);
                    if (ComponentManager.GetCptDef(cptDef.Uuid) == null)
                    {
                        ComponentManager.AddCptDefs(cptDef);
                    }
                }
            }

            if (docEle.TryGetProperty("ComponentInstances", out prop))
            {
                List<LcElement> eles = new List<LcElement>();
                var arr = prop.EnumerateArray().ToArray();
                for (int i = 0; i < arr.Length; i++)
                {
                    var item = arr[i];
                    IComponentInstance cptIns = null;
                    var typeName = item.ReadStringProperty("TypeName");
                    var baseType = item.ReadStringProperty("BaseType");
                    LcElement element;
                    if (baseType == nameof(LcComponentInstance) || baseType == nameof(DirectComponent))
                    {
                        var defId = item.ReadStringProperty("ComponentId");
                        var def = ComponentManager.GetCptDef(defId);
                        element = doc.CreateElement(typeName, baseType, def);
                    }
                    else
                    {
                        element = doc.CreateElement(typeName, baseType);
                    }
                    element.ReadBaseProperties(ref item);

                    cptIns = element as IComponentInstance;
                    cptIns.ReadProperties(ref item);
                    eles.Add(cptIns as LcElement);
                }
                var idDic = new Dictionary<long, long>();
                foreach (var ele in eles)
                {
                    long oldId = ele.Id;
                    ele.Initilize(doc);
                    idDic.Add(oldId, ele.Id);
                }
                foreach (var ele in eles)
                {
                   if(ele.Type.Name == "Window" || ele.Type.Name == "Door")
                    {
                        var oldWallId = Convert.ToInt64(ele.UserData["WallId"]);
                        ele.UserData["WallId"] = idDic[oldWallId];
                    }
                }
                LcDocument.ResetObjectRelation(eles);
                doc.ModelSpace.InsertElements(eles);
            }
        }


        /// <summary>
        /// UI及主要操作版本号
        /// #ToDo 
        /// </summary>
        /// 
        public const string Version = "1.0";

        /// <summary>
        /// 文件版本号(新增)
        /// #ToDo 需要考虑文件版本升级的兼容性
        /// </summary>
        public const string SerializationVersion = "1.0";

        //[JsonIgnore]
        //public static LcDocument Current { get; private set; }

        public static List<string> RegistAssemblies = new List<string>()
        {
            "LightCAD.Drawing.Actions"
        };

        /// <summary>
        /// 元素的活动处理器对象，由外部初始化确定
        /// </summary>
        public static Dictionary<ElementType, object> ElementActions { get; private set; } = new Dictionary<ElementType, object>();
        public static Dictionary<ElementType, object> Element3dActions { get; private set; } = new Dictionary<ElementType, object>();
        public static ElementTypeCollection ElementTypes = new ElementTypeCollection();

        #endregion

    }
}